package com.wzw.expression.record.model;

import com.wzw.expression.expression.invocation.OperationInvocationContext;
import com.wzw.expression.expression.invocation.OperationInvocationMetadata;
import com.wzw.expression.record.expression.IRecordOperation;
import com.wzw.expression.record.support.IRecordTraces;
import lombok.Data;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author Wangzhiwen
 */
public class IRecordContext extends OperationInvocationContext {

    private Operator operator;

    private String serial;

    private String content;

    private InnerRecord innerRecord;

    private ExtraRecord extraRecord;

    public IRecordContext(OperationInvocationMetadata metadata, Object target, Object[] args) {
        super(metadata, target, args);
    }

    public Operator getOperator() {
        return operator;
    }

    public void setOperator(Operator operator) {
        this.operator = operator;
    }

    public String getSerial() {
        return serial;
    }

    public void setSerial(String serial) {
        this.serial = serial;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public InnerRecord getInnerRecord() {
        return innerRecord;
    }

    public void setInnerRecord(InnerRecord innerRecord) {
        this.innerRecord = innerRecord;
    }

    public ExtraRecord getExtraRecord() {
        return extraRecord;
    }

    public void setExtraRecord(ExtraRecord extraRecord) {
        this.extraRecord = extraRecord;
    }

    @SuppressWarnings("unchecked")
    public <T> T getExtraRecord(Class<T> clazz) {
        if (Objects.nonNull(extraRecord)) {
            return (T) extraRecord;
        }
        try {
            return clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            return null;
        }
    }

    public void innerRecord() {
        innerRecord = new InnerRecord();
        innerRecord.setTraceId(IRecordTraces.getTraceId());
        innerRecord.setBeginTime(LocalDateTime.now());
        if (Objects.nonNull(args) && args.length != 0 && !((IRecordOperation) metadata.getOperation()).getIgnoreParams()) {
            List<MethodParam> params = Arrays.stream(args).map(MethodParam::new).collect(Collectors.toList());
            innerRecord.setMethodParams(params);
        }
        innerRecord.setMethodName(metadata.getTargetMethod().getName());
        innerRecord.setClassName(target.getClass().getName());
    }

    @Data
    @Accessors(chain = true)
    public static class InnerRecord implements Serializable {

        private String traceId;

        private String className;

        private String methodName;

        private List<MethodParam> methodParams;

        private LocalDateTime beginTime;

        private LocalDateTime endTime;
    }
}
